/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package gride.plugin;

import gride.GrideSettings;
import gride.events.EventManager;
import gride.exceptions.PluginException;
import gride.exceptions.XMLFormatException;
import gride.gui.GUIManager;
import gride.project.ProjectManager;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JToolBar;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * The PluginManager deals with loading all available plugins,
 * and provides an interface (a GUI component) for browsing,
 * activating and deactivating them
 *
 * @author lukasz
 */
public class PluginManager
{

    // <editor-fold defaultstate="expanded" desc="Members">

    private EventManager eventManager;
    private ProjectManager projectManager;
    private GUIManager guiManager;

    // list of all found plugins
    ArrayList<GridePlugin> plugins;

    // GUI components
    JPanel mainPanel;
    JToolBar toolbar;
    JList pluginList;
    JButton activateButton;
    JButton deactivateButton;
    JCheckBoxMenuItem menuItem;

    // </editor-fold>


    // <editor-fold defaultstate="expanded" desc="Constructors">

    /**
     * This constructor loads all the plugins found in the plugin directory,
     * activates them and sets up the PluginManager GUI component
     * @param eventManager
     * @param projectManager
     * @param guiManager
     */
    public PluginManager(EventManager eventManager,
                         ProjectManager projectManager, GUIManager guiManager)
    {
        this.eventManager = eventManager;
        this.projectManager = projectManager;
        this.guiManager = guiManager;

        // search plugin directory for plugins and load them
        plugins = new ArrayList<GridePlugin>();
        loadPlugins(new File(GrideSettings.PLUGINS_DIR));

        // activate plugins
        activatePlugins();

        // create a plugin panel and add an entry activating it to the menu
        createGUIComponents();
        guiManager.addMenuItem(menuItem, "Tools");
    }

    // </editor-fold>


    // <editor-fold defaultstate="expanded" desc="Plugin loading">

    private void activatePlugins()
    {
        // activate all plugins that we found
        for (GridePlugin plugin : plugins)
            if (plugin.getActivateOnStart())
                plugin.activate();
    }


    private void loadPlugins(File pluginDir)
    {        
        // filter only .xml files
        FilenameFilter filter = new FilenameFilter()
        {
            public boolean accept(File dir, String name)
            {
                return name.endsWith(".xml");
            }
        };

        // get the contents of the pluginDir
        File[] pluginFiles = pluginDir.listFiles(filter);

        // load each plugin
        for (File f : pluginFiles)
            load(f);
    }


    private void load(File pluginFile)
    {
        Boolean activate = true;
        String name = null;
        String version = null;
        String className = null;

        // parse the XML of the plugin file
        try
        {
            DocumentBuilder docBuilder =
                      DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = docBuilder.parse(pluginFile);

            // check root node has tag 'plugin'
            Element pluginNode = doc.getDocumentElement();
            if (!pluginNode.getNodeName().equals("plugin"))
              throw new XMLFormatException("Plugin XML file has wrong format!");

            // check whether we should activate this on start
            activate =
                    Boolean.parseBoolean(pluginNode.getAttribute("activate"));

            // read plugin name
            NodeList list = doc.getElementsByTagName("name");
            if (list.getLength() > 0)
                name = ((Element)list.item(0)).getTextContent();
            
            // read plugin version
            list = doc.getElementsByTagName("version");
            if (list.getLength() > 0)
                version  = ((Element)list.item(0)).getTextContent();

            // read plugin main class name
            list = doc.getElementsByTagName("main");
            if (list.getLength() == 1)
                className  = ((Element)list.item(0)).getTextContent();
            else
              throw new XMLFormatException("Plugin XML file has wrong format!");

        }
        catch (Exception ex)
        {
            System.out.println("Error loading " + pluginFile.getName() + " : " +
                               ex.getMessage());
        }

        // instantiate the plugin
        instantiatePlugin(className, name, version, activate);
    }


    private void instantiatePlugin(String className, String name,
                                   String version, boolean activate)
    {
        try
        {
            // find the specified class
            Class pluginClass = Class.forName(className);

            // check that it extends GridePlugin
            Class parentClass = pluginClass.getSuperclass();
            while (parentClass != null)
            {
                if (parentClass == GridePlugin.class)
                    break;

                parentClass = parentClass.getSuperclass();
            }

            if (parentClass == null)
                throw new PluginException("Error: Plugin class " + className +
                                          " doesn't inherit from GridePlugin!");

            // instantiate it
            GridePlugin plugin = (GridePlugin) pluginClass.newInstance();

            // set name and version of the plugin
            if (name != null)
                plugin.setName(name);

            if (version != null)
                plugin.setVersion(version);

            // set activateonstart property
            plugin.setActivateOnStart(activate);

            // give it references to event, project & gui managers
            plugin.setEventManager(eventManager);
            plugin.setProjectManager(projectManager);
            plugin.setGUIManager(guiManager);

            // add the plugin to the plugin list
            plugins.add(plugin);
        }
        catch (PluginException ex)
        {
            System.out.println(ex.getMessage());
        }
        catch (InstantiationException ex)
        {
            System.out.println("Error: couldn't instantiate class " +className);
        }
        catch (IllegalAccessException ex)
        {
            System.out.println("Error: couldn't instantiate class " +className);
        }
        catch (ClassNotFoundException ex)
        {
            System.out.println("Error: class " + className + " not found!");
        }

    }

    // </editor-fold>


    // <editor-fold defaultstate="expanded" desc="GUI component">

    private void createGUIComponents()
    {
        mainPanel = new JPanel(new BorderLayout());
        pluginList = new JList(plugins.toArray());
        toolbar = new JToolBar("Plugins toolbar");

        activateButton = new JButton("Activate");
        activateButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                GridePlugin plugin = (GridePlugin)pluginList.getSelectedValue();

                if (plugin != null && !plugin.isActive())
                {
                    plugin.activate();
                    pluginList.updateUI();
                }
            }
        });

        deactivateButton = new JButton("Deactivate");
        deactivateButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                GridePlugin plugin = (GridePlugin)pluginList.getSelectedValue();

                if (plugin != null && plugin.isActive())
                {
                    plugin.deactivate();
                    pluginList.updateUI();
                }
            }
        });

        toolbar.add(activateButton);
        toolbar.add(deactivateButton);

        mainPanel.add(toolbar, BorderLayout.NORTH);
        mainPanel.add(pluginList, BorderLayout.CENTER);


        // create the plugin manager menu entry
        menuItem = new JCheckBoxMenuItem("Plugin Manager");
        menuItem.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                if (menuItem.isSelected())
                    guiManager.addComponent(GUIManager.RIGHT_PANE, mainPanel,
                                            "Plugin Manager");
                else
                    guiManager.removeComponent(mainPanel);
            }
        });
    }

    // </editor-fold>
}
